﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using PacketAnalyzer.Data;

using System.Windows.Forms;

namespace PacketAnalyzer.Analyzer
{
    public class ZomojoAnalyzer : IAnalyzer
    {
        private static ZomojoAnalyzer instance = null;
        private static object syncRoot = new object();

        public static ZomojoAnalyzer GetInstance()
        {
            if (instance == null)
            {
                lock (syncRoot)
                {
                    if (instance == null)
                        instance = new ZomojoAnalyzer();
                }
            }

            return instance;
        }

        public ZomojoAnalyzer()
        {
        }

        public List<PacketData> Analyze(string packet)
        {
            if (packet.Length == 0)
                return null;

            if (packet.Contains("TCHODR"))
                return AnalyzeOrder(packet);
            else if (packet.Contains("TTRTDP"))
                return AnalyzeExecution(packet);
            else if (packet.Contains("TTRODP"))
                return AnalyzeConfirm(packet);

            return null;
        }

        public String GetRejectDescription(string rejectCode)
        {
            if (!ZomojoDescription.ErrorCode.ContainsKey(rejectCode)) {

                MessageBox.Show("Zomojo: " + rejectCode + " 주문 거부 코드가 존재하지 않습니다.");

                return "";
            }

            return ZomojoDescription.ErrorCode[rejectCode];
        }


        /// <summary>
        /// 주문 패킷 분석
        /// </summary>
        /// <param name="packet"></param>
        /// <returns></returns>
        public List<PacketData> AnalyzeOrder(string packet)
        {
            List<PacketData> r = new List<PacketData>();

            PacketData headerPacketData = new PacketData();

            int     pos = 0;
            int     dataCount = 1;
            bool    containsHeader = packet.Contains("TCHODR00000");

            if (containsHeader)
            {
                pos = packet.IndexOf("TCHODR00000") - 14;
                
                if (pos < 0)
                    return null;

                // Header 포함
                pos = headerPacketData.AddResult(packet, "전문 유형", 8, pos, null);
                pos = headerPacketData.AddResult(packet, "메시지 길이", PacketData.TYPE_NUMERIC, 6, pos, null);
                pos = headerPacketData.AddResult(packet, "메시지 타입", 11, pos, null);
                pos = headerPacketData.AddResult(packet, "일련 번호", 11, pos, null);
                pos = headerPacketData.AddResult(packet, "회원 번호", 5, pos, null);
                pos = headerPacketData.AddResult(packet, "연계시도착 회원사 번호", 10, pos, null);
                pos = headerPacketData.AddResult(packet, "회신시송신 회원사 번호", 10, pos, null);
                pos = headerPacketData.AddResult(packet, "전송 일시", 17, pos, null);
                pos = headerPacketData.AddResult(packet, "데이터 건수", PacketData.TYPE_NUMERIC, 3, pos, null);
                pos = headerPacketData.AddResult(packet, "암호화 유무", 1, pos, null);

                dataCount = int.Parse(headerPacketData.Get("데이터 건수"));
            }
            else
            {
                pos = packet.IndexOf("TCHODR1") - 11;

                if (pos < 0)
                    return null;
            }

            for (int i = 0; i < dataCount; i++)
            {
                PacketData packetData = new PacketData();

                //매매 관련 TR-CODE(TCHODR10001/TCHODR10002/TCHODR10003)
                pos = packetData.AddResult(packet, "메세지일련번호", 11, pos, null);
                pos = packetData.AddResult(packet, "트랜잭션코드", 11, pos, new Dictionary<string, string>() { { "TCHODR10001", "신규호가" }, { "TCHODR10002", "정정호가" }, { "TCHODR10003", "취소호가" } });
                pos = packetData.AddResult(packet, "보드ID", 2, pos, null);
                //pos = packetData.AddResult(packet, "정규시간외구분코드", 1, pos, new Dictionary<string, string>() { { "1", "정규장" }, { "2", "장개시전시간외종가" }, { "3", "장종료후시간외종가" }, { "4", "장종료후시간외단일가" } });
                pos = packetData.AddResult(packet, "회원번호", PacketData.TYPE_NUMERIC, 5, pos, null);
                pos = packetData.AddResult(packet, "지점번호", 5, pos, null);
                pos = packetData.AddResult(packet, "주문ID", PacketData.TYPE_NUMERIC, 10, pos, null);
                pos = packetData.AddResult(packet, "원주문ID", PacketData.TYPE_NUMERIC, 10, pos, null);
                pos = packetData.AddResult(packet, "종목코드", 12, pos, null);
                pos = packetData.AddResult(packet, "매도매수구분코드", 1, pos, new Dictionary<string, string>() { { "1", "매도" }, { "2", "매수" } });
                pos = packetData.AddResult(packet, "정정취소구분코드", 1, pos, new Dictionary<string, string>() { { "1", "신규" }, { "2", "정정" }, { "3", "취소" } });
                pos = packetData.AddResult(packet, "계좌번호", 12, pos, null);
                pos = packetData.AddResult(packet, "호가수량", PacketData.TYPE_NUMERIC, 10, pos, null);
                pos = packetData.AddResult(packet, "호가가격", PacketData.TYPE_NUMERIC, 11, pos, null);
                pos = packetData.AddResult(packet, "호가유형코드", 1, pos, new Dictionary<string, string>() { { "1", "시장가" }, { "2", "지정가" }, { "3", "경쟁대량" } });
                pos = packetData.AddResult(packet, "호가조건코드", 1, pos, new Dictionary<string, string>() { { "0", "일반 (FAS)" }, { "3", "IOC (FAK)" }, { "4", "FOK" } });
                pos = packetData.AddResult(packet, "시장조성자호가구분번호", 11, pos, new Dictionary<string, string>() { { "0", "일반" } });
                pos = packetData.AddResult(packet, "자사주신고서ID", 5, pos, null);
                pos = packetData.AddResult(packet, "자사주매매방법코드", 1, pos, new Dictionary<string, string>() { { "0", "해당없음" }, { "1", "자사주일반" }, { "2", "한은등 자사주" }, { "3", "정부등 자사주" } });
                pos = packetData.AddResult(packet, "매도유형코드", 2, pos, new Dictionary<string, string>() { { "00", "해당없음" }, { "01", "일반매도" }, { "02", "차입증권매도" }, { "06", "기타매도" } });
                pos = packetData.AddResult(packet, "신용구분코드", 2, pos, new Dictionary<string, string>() { { "10", "보통(일반)" }, { "21", "자기융자매수" }, { "22", "자기융자매도상환" }, { "23", "자기대주매도" }, { "24", "자기대주매수상환" }, { "31", "유통융자매수" }, { "32", "유통융자매도상환" }, { "33", "유통대주매도" }, { "34", "유통대주매수상환" } });
                pos = packetData.AddResult(packet, "위탁자기구분코드", 2, pos, new Dictionary<string, string>() { { "01", "현물" }, { "10", "위탁매매" }, { "30", "자기매매" }, { "11", "위탁거래" }, { "12", "위탁주선거래" }, { "31", "호가입력회원의 자기거래" }, { "32", "비회원의 자기거래" } });
                pos = packetData.AddResult(packet, "위탁사번호", PacketData.TYPE_NUMERIC, 5, pos, null);
                pos = packetData.AddResult(packet, "PT구분코드", 2, pos, new Dictionary<string, string>() { { "00", "일반" }, { "10", "차익거래(주식 및 주가지수)" }, { "20", "헤지거래(주식 및 주가지수)" } });
                pos = packetData.AddResult(packet, "대용주권계좌번호", 12, pos, null);
                pos = packetData.AddResult(packet, "계좌구분코드", 2, pos, new Dictionary<string, string>() { { "31", "위탁일반계좌" }, { "32", "옵션매수전용계좌" }, { "41", "자기일반계좌" }, { "42", "시장조성계좌" }, { "51", "헤지,차익거래계좌" } });
                pos = packetData.AddResult(packet, "계좌증거금유형코드", 2, pos, new Dictionary<string, string>() { { "10", "사전증거금 일반" }, { "11", "사후증거금 일반" }, { "12", "사후증거금 할인" } });
                pos = packetData.AddResult(packet, "국가코드", 3, pos, null);
                pos = packetData.AddResult(packet, "투자자구분코드", 4, pos, new Dictionary<string, string>() { { "1000", "증권회사 및 선물회사" }, { "2000", "보험회사" }, { "3000", "자산운용회사 및 투자회사" }, { "3100", "사모펀드" }, { "4000", "은행" }, { "5000", "종합금융회사, 상호저축은행" }, { "6000", "연금, 기금 및 공제회" }, { "7000", "국가, 지방자치단체 및 국제기구" }, { "7100", "기타법인" }, { "8000", "개인" } });
                pos = packetData.AddResult(packet, "외국인투자자구분코드", 2, pos, null);
                pos = packetData.AddResult(packet, "주문매체구분코드", 1, pos, new Dictionary<string, string>() { { "1", "영업점단말" }, { "2", "유선단말" }, { "3", "무선단말" }, { "4", "HTS" }, { "9", "기타" } });
                pos = packetData.AddResult(packet, "주문자식별정보", 12, pos, null);
                pos = packetData.AddResult(packet, "호가일자", 8, pos, null);
                pos = packetData.AddResult(packet, "회원사주문시각", 9, pos, null);
                pos = packetData.AddResult(packet, "회원사용영역", 40, pos, null);
                pos = packetData.AddResult(packet, "필러값", 73, pos, null);

                if (containsHeader)
                {
                    r.Add( ((PacketData)headerPacketData.Clone()).Merge(packetData));
                }
                else
                {
                    r.Add(packetData);
                }
            }

            return r;
        }

        /// <summary>
        /// 체결 패킷 분석
        /// </summary>
        /// <param name="packet"></param>
        /// <returns></returns>
        public List<PacketData> AnalyzeExecution(string packet)
        {
            List<PacketData> r = new List<PacketData>();

            PacketData headerPacketData = new PacketData();

            int     pos = 0;
            int     dataCount = 1;
            bool    containsHeader = packet.Contains("TCHTDP00000");

            if (containsHeader)
            {
                pos = packet.IndexOf("TCHTDP00000") - 14;

                if (pos < 0)
                    return null;

                // Header 포함
                pos = headerPacketData.AddResult(packet, "전문 유형", 8, pos, null);
                pos = headerPacketData.AddResult(packet, "메시지 길이", PacketData.TYPE_NUMERIC, 6, pos, null);
                pos = headerPacketData.AddResult(packet, "메시지 타입", 11, pos, null);
                pos = headerPacketData.AddResult(packet, "일련 번호", 11, pos, null);
                pos = headerPacketData.AddResult(packet, "회원 번호", 5, pos, null);
                pos = headerPacketData.AddResult(packet, "연계시도착 회원사 번호", 10, pos, null);
                pos = headerPacketData.AddResult(packet, "회신시송신 회원사 번호", 10, pos, null);
                pos = headerPacketData.AddResult(packet, "전송 일시", 17, pos, null);
                pos = headerPacketData.AddResult(packet, "데이터 건수", PacketData.TYPE_NUMERIC, 3, pos, null);
                pos = headerPacketData.AddResult(packet, "암호화 유무", 1, pos, null);

                dataCount = int.Parse(headerPacketData.Get("데이터 건수"));
            }
            else
            {
                pos = packet.IndexOf("TTRTDP") - 11;

                if (pos < 0)
                    return null;
            }

            for (int i = 0; i < dataCount; i++)
            {
                PacketData packetData = new PacketData();

                pos = packetData.AddResult(packet, "메세지일련번호", 11, pos, null);
                pos = packetData.AddResult(packet, "트랜잭션코드", 11, pos, null);
                pos = packetData.AddResult(packet, "정규시간외구분코드", 1, pos, new Dictionary<string, string>() { { "1", "정규장" }, { "2", "장개시전시간외종가" }, { "3", "장종료후시간외종가" }, { "4", "장종료후시간외단일가" } });
                pos = packetData.AddResult(packet, "회원번호", 5, pos, null);
                pos = packetData.AddResult(packet, "지점번호", 5, pos, null);
                pos = packetData.AddResult(packet, "주문ID", PacketData.TYPE_NUMERIC, 10, pos, null);
                pos = packetData.AddResult(packet, "원주문ID", PacketData.TYPE_NUMERIC, 10, pos, null);
                pos = packetData.AddResult(packet, "종목코드", 12, pos, null);
                pos = packetData.AddResult(packet, "체결번호", PacketData.TYPE_NUMERIC, 11, pos, null);
                pos = packetData.AddResult(packet, "체결가격", PacketData.TYPE_NUMERIC, 11, pos, null);
                pos = packetData.AddResult(packet, "체결수량", PacketData.TYPE_NUMERIC, 10, pos, null);
                pos = packetData.AddResult(packet, "체결유형코드", 2, pos, new Dictionary<string, string>() { { "01", "일반매매" }, { "10", "시가단일가" }, { "11", "시가단일가연장" }, { "20", "장중단일가" }, { "21", "장중단일가연장" }, { "30", "종가단일가" }, { "40", "접속" }, { "50", "장개시전시간외종가" }, { "60", "장종료후시간외종가개시" }, { "70", "시간외단일가" }, { "80", "단위매매체결" } });
                pos = packetData.AddResult(packet, "체결일자", 8, pos, null);
                pos = packetData.AddResult(packet, "체결시각", 9, pos, null);
                pos = packetData.AddResult(packet, "근월물체결가격", 11, pos, null);
                pos = packetData.AddResult(packet, "원월물체결가격", 11, pos, null);
                pos = packetData.AddResult(packet, "매도매수구분코드", 1, pos, new Dictionary<string, string>() { { "1", "매도" }, { "2", "매수" } });
                pos = packetData.AddResult(packet, "계좌번호", 12, pos, null);
                pos = packetData.AddResult(packet, "시장조성자호가구분번호", 11, pos, new Dictionary<string, string>() { { "0", "일반" } });
                pos = packetData.AddResult(packet, "위탁사번호", 5, pos, null);
                pos = packetData.AddResult(packet, "대용주권계좌번호", 12, pos, null);
                pos = packetData.AddResult(packet, "회원사용영역", 40, pos, null);
                pos = packetData.AddResult(packet, "필러값", 81, pos, null);

                if (containsHeader)
                {
                    r.Add( ((PacketData)headerPacketData.Clone()).Merge(packetData));
                }
                else
                {
                    r.Add(packetData);
                }
            }

            return r;
        }

        /// <summary>
        /// 처리 호가 분석
        /// </summary>
        /// <param name="packet"></param>
        /// <returns></returns>
        public List<PacketData> AnalyzeConfirm(string packet)
        {
            List<PacketData> r = new List<PacketData>();

            PacketData headerPacketData = new PacketData();

            int     pos = 0;
            int     dataCount = 1;
            bool    containsHeader = packet.Contains("TCHTDP00000");

            if (containsHeader)
            {
                pos = packet.IndexOf("TCHTDP00000") - 14;

                if (pos < 0)
                    return null;

                // Header 포함
                pos = headerPacketData.AddResult(packet, "전문 유형", 8, pos, null);
                pos = headerPacketData.AddResult(packet, "메시지 길이", 6, pos, null);
                pos = headerPacketData.AddResult(packet, "메시지 타입", 11, pos, null);
                pos = headerPacketData.AddResult(packet, "일련 번호", 11, pos, null);
                pos = headerPacketData.AddResult(packet, "회원 번호", 5, pos, null);
                pos = headerPacketData.AddResult(packet, "연계시도착 회원사 번호", 10, pos, null);
                pos = headerPacketData.AddResult(packet, "회신시송신 회원사 번호", 10, pos, null);
                pos = headerPacketData.AddResult(packet, "전송 일시", 17, pos, null);
                pos = headerPacketData.AddResult(packet, "데이터 건수", 3, pos, null);
                pos = headerPacketData.AddResult(packet, "암호화 유무", 1, pos, null);
            
                dataCount = int.Parse( headerPacketData.Get("데이터 건수") );
            }
            else
            {
                pos = packet.IndexOf("TTRODP") - 11;

                if (pos < 0)
                    return null;
            }

            for (int i = 0; i < dataCount; i++)
            {
                PacketData packetData = new PacketData();

                pos = packetData.AddResult(packet, "메세지일련번호", 11, pos, null);
                pos = packetData.AddResult(packet, "트랜잭션코드", 11, pos, new Dictionary<string, string>() { { "TTRODP11301", "취소" }, { "TTRODP11321", "거부" }, { "TTRODP11303", "자동취소" } });
                pos = packetData.AddResult(packet, "정규시간외구분코드", 1, pos, new Dictionary<string, string>() { { "1", "정규장" }, { "2", "장개시전시간외종가" }, { "3", "장종료후시간외종가" }, { "4", "장종료후시간외단일가" } });
                pos = packetData.AddResult(packet, "회원번호", 5, pos, null);
                pos = packetData.AddResult(packet, "지점번호", 5, pos, null);
                pos = packetData.AddResult(packet, "주문ID", 10, pos, null);
                pos = packetData.AddResult(packet, "원주문ID", 10, pos, null);
                pos = packetData.AddResult(packet, "종목코드", 12, pos, null);
                pos = packetData.AddResult(packet, "매도매수구분코드", 1, pos, new Dictionary<string, string>() { { "1", "매도" }, { "2", "매수" } });
                pos = packetData.AddResult(packet, "정정취소구분코드", 1, pos, new Dictionary<string, string>() { { "1", "신규" }, { "2", "정정" }, { "3", "취소" } });
                pos = packetData.AddResult(packet, "계좌번호", 12, pos, null);
                pos = packetData.AddResult(packet, "호가수량", 10, pos, null);
                pos = packetData.AddResult(packet, "호가가격", 11, pos, null);
                pos = packetData.AddResult(packet, "호가유형코드", 1, pos, new Dictionary<string, string>() { { "1", "시장가" }, { "2", "지정가" } });
                pos = packetData.AddResult(packet, "호가조건코드", 1, pos, new Dictionary<string, string>() { { "0", "일반 (FAS)" }, { "3", "IOC (FAK)" }, { "4", "FOK" } });
                pos = packetData.AddResult(packet, "시장조성자호가구분번호", 11, pos, null);
                pos = packetData.AddResult(packet, "자사주신고서ID", 5, pos, null);
                pos = packetData.AddResult(packet, "자사주매매방법코드", 1, pos, null);
                pos = packetData.AddResult(packet, "매도유형코드", 2, pos, new Dictionary<string, string>() { { "00", "해당없음" }, { "01", "일반매도" }, { "02", "차입증권매도" }, { "06", "기타매도" } });
                pos = packetData.AddResult(packet, "신용구분코드", 2, pos, new Dictionary<string, string>() { { "10", "보통(일반)" }, { "21", "자기융자매수" }, { "22", "자기융자매도상환" }, { "23", "자기대주매도" }, { "24", "자기대주매수상환" }, { "31", "유통융자매수" }, { "32", "유통융자매도상환" }, { "33", "유통대주매도" }, { "34", "유통대주매수상환" } });
                pos = packetData.AddResult(packet, "위탁자기구분코드", 2, pos, new Dictionary<string, string>() { { "11", "위탁거래" }, { "12", "위탁주선거래" }, { "31", "호가입력회원의 자기거래" }, { "32", "비회원의 자기거래" } });
                pos = packetData.AddResult(packet, "위탁사번호", 5, pos, null);
                pos = packetData.AddResult(packet, "PT구분코드", 2, pos, new Dictionary<string, string>() { { "00", "일반" }, { "10", "차익거래(주식 및 주가지수)" }, { "20", "헤지거래(주식 및 주가지수)" } });
                pos = packetData.AddResult(packet, "대용주권계좌번호", 12, pos, null);
                pos = packetData.AddResult(packet, "계좌구분코드", 2, pos, new Dictionary<string, string>() { { "31", "위탁일반계좌" }, { "32", "옵션매수전용계좌" }, { "41", "자기일반계좌" }, { "42", "시장조성계좌" }, { "51", "헤지,차익거래계좌" } });
                pos = packetData.AddResult(packet, "계좌증거금유형코드", 2, pos, new Dictionary<string, string>() { { "10", "사전증거금 일반" }, { "11", "사후증거금 일반" }, { "12", "사후증거금 할인" } });
                pos = packetData.AddResult(packet, "국가코드", 3, pos, null);
                pos = packetData.AddResult(packet, "투자자구분코드", 4, pos, new Dictionary<string, string>() { { "1000", "증권회사 및 선물회사" }, { "2000", "보험회사" }, { "3000", "자산운용회사 및 투자회사" }, { "3100", "사모펀드" }, { "4000", "은행" }, { "5000", "종합금융회사, 상호저축은행" }, { "6000", "연금, 기금 및 공제회" }, { "7000", "국가, 지방자치단체 및 국제기구" }, { "7100", "기타법인" }, { "8000", "개인" } });
                pos = packetData.AddResult(packet, "외국인투자자구분코드", 2, pos, new Dictionary<string, string>() { { "00", "외국인아님" }, { "10", "외국인(거주)" }, { "20", "외국인(비거주)" } });
                pos = packetData.AddResult(packet, "주문매체구분코드", 1, pos, new Dictionary<string, string>() { { "1", "영업점단말" }, { "2", "유선단말" }, { "3", "무선단말" }, { "4", "HTS" }, { "9", "기타" } });
                pos = packetData.AddResult(packet, "주문자식별정보", 12, pos, null);
                pos = packetData.AddResult(packet, "호가일자", 8, pos, null);
                pos = packetData.AddResult(packet, "회원사주문시각", 9, pos, null);
                pos = packetData.AddResult(packet, "회원사용영역", 40, pos, null);
                pos = packetData.AddResult(packet, "실정정취소호가수량", 10, pos, null);
                pos = packetData.AddResult(packet, "자동취소처리구분코드", 1, pos, new Dictionary<string, string>() { { "0", "해당없음" }, { "1", "조건호가취소처리" }, { "2", "일괄취소처리" } });
                pos = packetData.AddResult(packet, "호가거부사유코드", 4, pos, ZomojoDescription.ErrorCode);
                pos = packetData.AddResult(packet, "필러값", 58, pos, null);

                if (containsHeader)
                {
                    r.Add( ((PacketData)headerPacketData.Clone()).Merge(packetData));
                }
                else
                {
                    r.Add(packetData);
                }
            }

            return r;
        }
    }
}
